package com.app.server;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;

import com.app.game.chat.Message;
import com.app.game.other.Credit;
import com.app.game.other.Player;
import com.app.game.other.PlayerFix;
import com.app.head.Head;
import com.app.head.HeadsConst;
import com.app.head.impl.*;
import com.app.lan.Receiver;
import com.app.lan.Sender;
import com.app.observers.ClientObserver;
import com.app.observers.ClientSubject;
import com.app.task.Task;
import java.io.*;
import java.util.logging.Level;
import java.util.logging.Logger;

public class ClientHandler extends Task implements ClientSubject {

    private Socket socket;
    private ObjectInputStream input;
    private ObjectOutputStream output;
    private ArrayList<ClientObserver> observers = new ArrayList<ClientObserver>();
    private Player player;
    private Integer id;

    public ClientHandler(Socket socket, Integer id) {
        this.id = id;
        this.socket = socket;

    }

    @Override
    protected void task() {
        try {
        	Head head;
            if((head = (Head) input.readObject()) !=null){
            int option = head.getObjectId();

            switch (option) {
                case HeadsConst.PLAYER:
                    playerDelivery((Player) head.getObject());
                    break;
                case HeadsConst.ROUND_DONE:
                    roundDoneDelivery();
                    break;
                case HeadsConst.READY:
                    playerIsReadyDelivery();
                    break;
                case HeadsConst.MESSAGE:
                    messageDelivery((Message) head.getObject());
                    break;
                case HeadsConst.PLAYER_UPDATE:
                    updatedPlayerDelivery((PlayerFix) head.getObject());break;
                case HeadsConst.PLAYER_LEFT:
                	playerLeftDelivery((Integer)head.getObject());break;

            }
            }

        } catch (Exception e) {
        	closeConnection();
        }
    }

    private synchronized void playerDelivery(Player player) throws IOException, ClassNotFoundException {
        this.player = player;

        Credit credit = new Credit();
        credit.setValue(1000);
        player.setId(id);
        player.setCredits(credit);
        HeadPlayer headPlayer = new HeadPlayer();
        headPlayer.setObject(player);


        try {
            output.writeObject(headPlayer);

        } catch (Exception e) {
        }
        for (ClientObserver ob : observers) {
            ob.updatePlayer(player, id);
        }
    }

    private void updatedPlayerDelivery(PlayerFix player) {
        for (ClientObserver o : observers) {
            if(player!=null)
        	o.reupdatePlayer(player);
        }
    }

    private void roundDoneDelivery() {
        for (ClientObserver ob : observers) {
            ob.updateRoundDone(id);
        }
    }

    private void playerIsReadyDelivery() {
        for (ClientObserver ob : observers) {
            ob.updateReady(id);
        }
    }

    private void messageDelivery(Message msg) throws IOException, ClassNotFoundException {

        for (ClientObserver ob : observers) {
            ob.updateMessage(msg);
        }
    }
    
    private void playerLeftDelivery(Integer playerId){
    	for(ClientObserver ob: observers){
    		ob.updatePlayerLeft(playerId);
    	}
    	closeConnection();
    	
    }
    public void sendPlayerlist(HashMap<Integer, Player> playerList) {
        HeadPlayerList headPlayerList = new HeadPlayerList();
        headPlayerList.setObject(playerList);
        try {
            output.writeObject(headPlayerList);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    public void sendGameIsStarting() {
        HeadGameStarting headGameStart = new HeadGameStarting();
        headGameStart.setObject(true);
        try {
            output.writeObject(headGameStart);
        } catch (IOException e) {

            e.printStackTrace();
        }
    }

    public void sendGameIsEnding() {
        HeadGameOver headGameOver = new HeadGameOver();
        headGameOver.setObject(true);
        //headSender.setObject(headGameOver);
        try {
            output.writeObject(headGameOver);
        } catch (IOException e) {

            e.printStackTrace();
        }
        closeConnection();
        
    }

    public void sendResult(Integer result) {
        HeadResult headResult = new HeadResult();
        headResult.setObject(result);

        try {
            output.writeObject(headResult);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    public void sendReadyNotification(Integer playerId) {
        HeadReadyNotification headReadyNotification = new HeadReadyNotification();
        headReadyNotification.setObject(playerId);
        try {
            output.writeObject(headReadyNotification);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void sendMessage(Message msg) {
        HeadMessage headMessage = new HeadMessage();
        headMessage.setObject(msg);
        try {
            output.writeObject(headMessage);
        } catch (IOException ex) {
            Logger.getLogger(ClientHandler.class.getName()).log(Level.SEVERE, null, ex);
        }

    }

    public void sendRoundDoneNotification(Integer playerId) {
        HeadRoundDone headRoundDone = new HeadRoundDone();
        headRoundDone.setObject(playerId);
        try {
            output.writeObject(headRoundDone);
        } catch (IOException ex) {
            Logger.getLogger(ClientHandler.class.getName()).log(Level.SEVERE, null, ex);
        }

    }
    public void sendUpdatedPlayer(PlayerFix player){
        if(player==null){
        	return;
        }
    	HeadUpdatedPlayer hup = new HeadUpdatedPlayer();
        hup.setObject(player);
        try {
            output.writeObject(hup);
        } catch (IOException ex) {
            Logger.getLogger(ClientHandler.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    public void sendPlayerLeftNotification(Integer playerId){
    	HeadPlayerLeft hpl = new HeadPlayerLeft();
    	hpl.setObject(playerId);
    	
    		try {
				output.writeObject(hpl);
			} catch (IOException e) {
			
				e.printStackTrace();
			}
    	
    }

    @Override
    public void registerObserver(ClientObserver observer) {
        observers.add(observer);
    }

    @Override
    public void removeObserver(ClientObserver observer) {
        observers.remove(observer);
    }

    @Override
    public void notifyObservers() {
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }
   

    @Override
    public void beforeTask() {
        try {
            output = new ObjectOutputStream(socket.getOutputStream());
            input = new ObjectInputStream(socket.getInputStream());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    private void closeConnection(){
    	stop();
    	try{
    	input.close();
    	output.close();
    	socket.close();
    	}catch(IOException e){
    		e.printStackTrace();
    	}
    }
}
